package cn.getech.data.development.service.impl;

import cn.getech.data.development.config.properties.BdpJobConfig;
import cn.getech.data.development.config.properties.DataDevelopmentConfig;
import cn.getech.data.development.entity.ProcInfo;
import cn.getech.data.development.entity.RealTimeTask;
import cn.getech.data.development.entity.RealtimeResourceEntity;
import cn.getech.data.development.mapper.RealTimeTaskMapper;
import cn.getech.data.development.mapper.RealtimeResourceMapper;
import cn.getech.data.development.model.dto.RealtimeResourceDto;
import cn.getech.data.development.model.qo.RealtimeResourceSearchQO;
import cn.getech.data.development.model.req.realtimeresource.RealtimeResourceListReq;
import cn.getech.data.development.model.res.realtimeresource.RealtimeResourceListRes;
import cn.getech.data.development.service.ProcInfoService;
import cn.getech.data.development.service.RealTimeTaskMenuService;
import cn.getech.data.development.service.RealTimeTaskService;
import cn.getech.data.development.service.RealtimeResourceService;
import cn.getech.data.development.utils.HdfsUtil;
import cn.getech.data.intelligence.common.exception.RRException;
import cn.getech.data.intelligence.common.utils.PageUtils2;
import cn.getech.data.intelligence.common.utils.Query;
import cn.getech.system.center.service.SysRoleService;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.util.UriUtils;

import javax.activation.MimetypesFileTypeMap;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.*;
import java.util.stream.Collectors;


@Slf4j
@Service("realtimeResourceService")
public class RealtimeResourceServiceImpl extends ServiceImpl<RealtimeResourceMapper, RealtimeResourceEntity> implements RealtimeResourceService {
    @Autowired
    private BdpJobConfig bdpJobConfig;
    @Autowired
    private SysRoleService sysRoleService;
    @Autowired
    private ProcInfoService procInfoService;

    @Autowired
    private RealTimeTaskService realTimeTaskService;

    @Autowired
    private RealTimeTaskMenuService realTimeTaskMenuService;
    @Resource
    private RealTimeTaskMapper taskMapper;
    @Autowired
    private DataDevelopmentConfig dataDevelopmentConfig;

    private static final String repCode = "ABCDQEROO";

    @Override
    public PageUtils2<RealtimeResourceEntity> queryPage(Map<String, Object> params) {
        IPage<RealtimeResourceEntity> page = this.page(
                new Query<RealtimeResourceEntity>().getPage(params),
                new QueryWrapper<RealtimeResourceEntity>()
        );

        return new PageUtils2<RealtimeResourceEntity>(page);
    }

    @Override
    public PageUtils2<RealtimeResourceListRes> queryPage(RealtimeResourceListReq req, Long userId) {
        Map<String, Object> params = new HashMap();
        Long procId = req.getProcId();
        params.put("procId", procId);
        if (StringUtils.isNotEmpty(req.getResourceName())) {
            params.put("resourceName", req.getResourceName());
        }
        if (StringUtils.isNotEmpty(req.getUsername())) {
            params.put("username", req.getUsername());
        }
        if (req.getTypeResource() != null) {
            params.put("typeResource", req.getTypeResource());
        }
        if (req.getLimit() != null && req.getPage() != null) {
            params.put("offset", (req.getPage() - 1) * req.getLimit());
            params.put("limit", req.getLimit());
        }

        List<RealtimeResourceListRes> list = this.baseMapper.listBy(params);
        if(CollectionUtil.isNotEmpty(list)){
            for (RealtimeResourceListRes realtimeResourceListRes : list) {
                if (StringUtils.isNotEmpty(realtimeResourceListRes.getResourceFilename()) &&
                        realtimeResourceListRes.getResourceFilename().contains("/")) {
                    String name = realtimeResourceListRes.getResourceFilename().substring(
                            realtimeResourceListRes.getResourceFilename().indexOf("/") + 1);
                    if(StringUtils.isNotEmpty(name)){
                        realtimeResourceListRes.setResourceFilename(name.replaceAll(repCode,"%"));
                    }else{
                        realtimeResourceListRes.setResourceFilename(name);
                    }
                }
            }
        }
        int count = this.baseMapper.listByCount(params);
        PageUtils2<RealtimeResourceListRes> pageUtils = new PageUtils2(list, count, req.getLimit(), req.getPage());
        return pageUtils;
    }

    @Override
    public PageUtils2<RealtimeResourceListRes> queryPageByProc(RealtimeResourceListReq req, Long userId) {
        Map<String, Object> params = new HashMap<>();
        Long procId = req.getProcId();
        ProcInfo procInfo = procInfoService.getById(procId);
        if (procInfo != null) {
            if (!procInfo.getCreatePer().equals(userId.intValue())) {
                params.put("userId", userId);
            }
        }
        params.put("procId", procId);
        params.put("realResource", req.getTypeResource());
        if (req.getResourceType() == null) {
            req.setFileExtension("jar");
        }
        params.put("fileExtension", req.getFileExtension());
        params.put("resourceType", req.getResourceType());
        if (StringUtils.isNotEmpty(req.getResourceName())) {
            params.put("resourceName", req.getResourceName());
        }
        if (StringUtils.isNotEmpty(req.getUsername())) {
            params.put("username", req.getUsername());
        }

        if (req.getLimit() != null && req.getPage() != null) {
            params.put("offset", (req.getPage() - 1) * req.getLimit());
            params.put("limit", req.getLimit());
        }

        List<RealtimeResourceListRes> list = this.baseMapper.listBy(params);
//        for (RealtimeResourceListRes realtimeResourceListRes : list) {
//            if (StringUtils.isNotEmpty(realtimeResourceListRes.getResourceFilename()) &&
//                    realtimeResourceListRes.getResourceFilename().contains("/")) {
//                String name = realtimeResourceListRes.getResourceFilename().substring(
//                        realtimeResourceListRes.getResourceFilename().indexOf("/") + 1);
//                realtimeResourceListRes.setResourceFilename(name);
//            }
//        }
        if (req.getMenuId() != null) {//任务id不空  讲任务的资源添加上 任务详情需要回显
            RealTimeTask task = taskMapper.selectOne(new QueryWrapper<RealTimeTask>()
                    .eq("menu_id", req.getMenuId()));
            if (CollectionUtils.isEmpty(list)) {
                list = new ArrayList<>();
            }
            if (task != null && task.getResourceId() != null) {
                Long sid = Long.parseLong(task.getResourceId().toString());
                Set<Long> sourceIds = list.stream().map(RealtimeResourceListRes::getResourceId)
                        .collect(Collectors.toSet());
                if (!sourceIds.contains(sid)) {
                    RealtimeResourceEntity resource = baseMapper.selectById(task.getResourceId());
                    if (resource != null) {
                        RealtimeResourceListRes res = new RealtimeResourceListRes();
                        res.setResourceName(resource.getResourceName());
                        res.setResourceId(resource.getResourceId());
                        res.setResourceFilename(resource.getResourceFilename());
                        res.setResourceType(resource.getResourceType());
                        list.add(res);
                    }
                }
            }
        }
        int count = this.baseMapper.listByCount(params);
        PageUtils2<RealtimeResourceListRes> pageUtils = new PageUtils2(list, count, req.getLimit(), req.getPage());
        return pageUtils;
    }

    @Override
    public Long save(Long procId, String resourceName, String fileExtension, String desc, Integer typeResource, MultipartFile multipartFile, Long userId) {
        if (desc != null && desc.length() > 100)
            throw new RRException("描述不能超过100个字");
        String jarOriginName = String.format("%s/%s", procId, multipartFile.getOriginalFilename().trim());
        if (jarOriginName.contains("-") ||
                jarOriginName.contains(" ")
                || jarOriginName.contains("(")
                || jarOriginName.contains(")")) {
            throw new RRException("文件名中不能包含横线,空格,括号等特殊字符");
        }
        //将%替换成其他的特殊标识
        jarOriginName = jarOriginName.replaceAll("%",repCode);
        RealtimeResourceEntity realtimeResourceEntity = new RealtimeResourceEntity();
        realtimeResourceEntity.setProcId(procId);
        realtimeResourceEntity.setResourceName(resourceName);
        realtimeResourceEntity.setFileExtension(fileExtension);
        realtimeResourceEntity.setResourceFilename(jarOriginName);
        realtimeResourceEntity.setResourceDesc(desc);
        realtimeResourceEntity.setTypeResource(typeResource);
        realtimeResourceEntity.setCreatePer(userId);
        realtimeResourceEntity.setCreateTime(new Date());
        int oldCount = baseMapper.selectCount(new QueryWrapper<RealtimeResourceEntity>().eq("resource_name", resourceName));
        if (oldCount > 0) {
            throw new RRException("已经存在该资源名称，请使用其他资源名称");
        }
        RealtimeResourceEntity fileOld = baseMapper.selectOne(new QueryWrapper<RealtimeResourceEntity>()
                .eq("resource_filename", jarOriginName).last("limit 1"));
        if (fileOld != null) {
            baseMapper.update(realtimeResourceEntity, new QueryWrapper<RealtimeResourceEntity>()
                    .eq("resource_filename", jarOriginName));
        }else{
            baseMapper.insert(realtimeResourceEntity);
        }
        uploadJar(multipartFile, jarOriginName);
        return realtimeResourceEntity.getResourceId();
    }

    @Override
    public boolean deleteResource(Long id, Long userId) {
        RealtimeResourceEntity old = getById(id);
        if (old == null)
            throw new RRException("没有该记录");
        if (!procInfoService.isProcOwer(userId, old.getProcId())
                && userId.longValue() != old.getCreatePer().longValue())
            throw new RRException("不是自己创建的不能删除");
        Integer num = baseMapper.isJobUse(id);
        if (num != null && num > 0) {
            QueryWrapper queryWrapper = new QueryWrapper();
            queryWrapper.eq("resource_id", id);
            String resourceFileName = old.getResourceFilename();
            List<RealTimeTask> realTimeTaskList = realTimeTaskService.list(queryWrapper);
            List<Integer> menudIdList = realTimeTaskList.stream().map(e -> e.getMenuId()).distinct().collect(Collectors.toList());
            Long count = realTimeTaskMenuService.listByIds(menudIdList).stream().filter(e -> e.getState() != null && e.getState() == 1).count();
            if (count > 0) {
                String menuName = realTimeTaskMenuService.listByIds(menudIdList).stream().filter(e -> e.getState() != null && e.getState() == 1).map(e -> e.getName()).collect(Collectors.joining("、"));
                String tip = "实时资源【" + resourceFileName + "】已被【" + menuName + "】引用,暂不能删除";
                /**
                 *  “实时资源【实时jar包名称】已被【实时任务名称1、实时任务名称2、实时任务名称3】引用，暂不能删除”
                 */
                throw new RRException(tip);
            }

        }
        removeByIds(Arrays.asList(id));
        deleteJar(old.getResourceFilename());
        return true;
    }

    @Value("${spring.bdpjob.realtime:/bdp/realtime}")
    private String realTime;
    @Override
    public List<RealtimeResourceDto> listAll(RealtimeResourceSearchQO realtimeResourceSearchQO) {
        QueryWrapper<RealtimeResourceEntity> realtimeResourceEntityQueryWrapper = new QueryWrapper<>();
        if (null == realtimeResourceSearchQO || null == realtimeResourceSearchQO.getProcId()) {
            throw new RRException("项目id必传！");
        }
        if (null != realtimeResourceSearchQO && StringUtils.isNotEmpty(realtimeResourceSearchQO.getResourceName())) {
            realtimeResourceEntityQueryWrapper.like("resource_name", realtimeResourceSearchQO.getResourceName());
        }
        if (null != realtimeResourceSearchQO && null != realtimeResourceSearchQO.getTypeResource() && CollectionUtil.isNotEmpty(realtimeResourceSearchQO.getTypeResource())) {
            realtimeResourceEntityQueryWrapper.in("type_resource", realtimeResourceSearchQO.getTypeResource());
        }
        realtimeResourceEntityQueryWrapper.eq("proc_id", realtimeResourceSearchQO.getProcId());
        realtimeResourceEntityQueryWrapper.orderByDesc("create_time");
        List<RealtimeResourceEntity> realtimeResourceEntities = baseMapper.selectList(realtimeResourceEntityQueryWrapper);
        if (CollectionUtil.isNotEmpty(realtimeResourceEntities)) {
            List<RealtimeResourceDto> realtimeResourceDtos = new ArrayList<>();
            for (RealtimeResourceEntity realtimeResourceEntity : realtimeResourceEntities) {
                RealtimeResourceDto realtimeResourceDto = new RealtimeResourceDto();
                BeanUtils.copyProperties(realtimeResourceEntity, realtimeResourceDto);
                if (Integer.valueOf(1).equals(realtimeResourceEntity.getTypeResource())) {//实时资源
                    realtimeResourceDto.setUseAddress(realTime+"/" + realtimeResourceEntity.getResourceFilename());
                }else{
                    realtimeResourceDto.setUseAddress("${bdp.hdfs.file." + realtimeResourceEntity.getResourceId() + "}");
                }
                realtimeResourceDtos.add(realtimeResourceDto);
            }
            return realtimeResourceDtos;
        }
        return null;
    }

    @Override
    public void download(Long resourceId, HttpServletResponse response) {
        RealtimeResourceEntity realtimeResourceEntity = baseMapper.selectById(resourceId);
        if (realtimeResourceEntity == null)
            throw new RRException("没有该记录");

        download(realtimeResourceEntity.getResourceFilename(), response);
    }

    private void download(String jarName, HttpServletResponse res) {
        HdfsUtil hdfsUtil = null;
        BufferedInputStream bis = null;
        try {
            hdfsUtil = new HdfsUtil(bdpJobConfig);
            String uploadPath = String.format("%s/%s", bdpJobConfig.getRealtime(), jarName);
            bis = hdfsUtil.readFile(uploadPath, res);
            if (bis != null) {
                // 设置信息给客户端不解析
                String filename = jarName.substring(jarName.indexOf("/") + 1);
                filename = filename.replaceAll(repCode,"%");
                String type = new MimetypesFileTypeMap().getContentType(filename);
                // 设置contenttype，即告诉客户端所发送的数据属于什么类型
                res.setHeader("Content-type", type);
                // 设置编码
                String hehe = new String(filename.getBytes("utf-8"), "iso-8859-1");
                // 设置扩展头，当Content-Type 的类型为要下载的类型时 , 这个信息头会告诉浏览器这个文件的名字和类型。
                res.setHeader("Content-Disposition", "attachment;filename=" + hehe);
                res.setHeader("filename", hehe);

                OutputStream outputStream = res.getOutputStream();
                byte[] buff = new byte[1024];
                int i = bis.read(buff);
                while (i != -1) {
                    outputStream.write(buff, 0, buff.length);
                    outputStream.flush();
                    i = bis.read(buff);
                }
            }
        } catch (Exception e) {
            log.error("下载" + jarName + "失败：", e);
            throw new RRException("下载" + jarName + "失败");
        } finally {
            if (hdfsUtil != null) {
                hdfsUtil.close();
            }
            if (bis != null) {
                try {
                    bis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private void uploadJar(MultipartFile multipartFile, String jarName) {
        HdfsUtil hdfsUtil = null;
        try {
            hdfsUtil = new HdfsUtil(bdpJobConfig);
            String uploadPath = String.format("%s/%s", bdpJobConfig.getRealtime(), jarName);
            hdfsUtil.writeFile(multipartFile.getBytes(), uploadPath);
        } catch (Exception e) {
            log.error("上传" + multipartFile.getOriginalFilename() + "失败：" + e.getMessage());
            throw new RRException("上传" + multipartFile.getOriginalFilename() + "失败");
        } finally {
            if (hdfsUtil != null) {
                hdfsUtil.close();
            }
        }
    }

    private void deleteJar(String jarName) {
        HdfsUtil hdfsUtil = null;
        try {
            hdfsUtil = new HdfsUtil(bdpJobConfig);
            String path = String.format("%s/%s", bdpJobConfig.getRealtime(), jarName);
            hdfsUtil.delete(path);
        } catch (Exception e) {
            log.error("删除" + jarName + "失败：" + e.getMessage());
            throw new RRException("删除" + jarName + "失败");
        } finally {
            if (hdfsUtil != null) {
                hdfsUtil.close();
            }
        }
    }
}
